/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#pragma once

extern cvar_t *gl_cull;

extern ID3D10DepthStencilState *d3d_ZmodeNormal;
extern ID3D10DepthStencilState *d3d_ZmodeNowrite;
extern ID3D10DepthStencilState *d3d_ZmodeNone;

extern ID3D10BlendState *d3d_BlendNone;
extern ID3D10BlendState *d3d_BlendAdditive;
extern ID3D10BlendState *d3d_BlendMultiply;
extern ID3D10BlendState *d3d_BlendMax;
extern ID3D10BlendState *d3d_BlendMin;
extern ID3D10BlendState *d3d_BlendAlpha;
extern ID3D10BlendState *d3d_BlendZFill;

extern ID3D10RasterizerState *d3d_RasterizerDefault;
extern ID3D10RasterizerState *d3d_RasterizerFlipCull;


/*
============================================================================================================

		STATE FILTERING

============================================================================================================
*/

enum blendmode_t
{
	BM_NONE,
	BM_ALPHA,
	BM_ADDITIVE,
	BM_MULTIPLY,
	BM_MAX
};


enum zmode_t
{
	ZM_NORMAL,
	ZM_NOWRITE,
	ZM_NONE
};


struct renderstate_t
{
	blendmode_t blend;
	zmode_t zmode;
};


extern renderstate_t rs_default;
extern renderstate_t rs_additive;
extern renderstate_t rs_lumablend;
extern renderstate_t rs_drawblended;
extern renderstate_t rs_drawnoblend;
extern renderstate_t rs_alphablend;

class QSTATEFILTER
{
public:
	QSTATEFILTER (void);
	void ClearState (void);

	__inline void OMSetBlendState (ID3D10BlendState *bs)
	{
		if (this->BlendState != bs)
		{
			float junk[4] = {1, 1, 1, 1};
			this->OnStateChange ();
			d3d_Device->OMSetBlendState (bs, junk, 0xffffffff);
			this->BlendState = bs;
		}
	}

	__inline void OMSetDepthState (ID3D10DepthStencilState *ds, UINT ref = 0)
	{
		if (this->DepthState != ds || this->StencilRef != ref)
		{
			// we always use a stencil ref of zero so lights will be drawn where the stencil buffer is untouched
			this->OnStateChange ();
			d3d_Device->OMSetDepthStencilState (ds, ref);
			this->DepthState = ds;
			this->StencilRef = ref;
		}
	}

	__inline void VSSetTexture (int slot, QTEXTURE *Texture)
	{
		if (this->VSTextures[slot] != Texture)
		{
			this->OnStateChange ();
			d3d_Device->VSSetShaderResources (slot, 1, &Texture->SRV);
			this->VSTextures[slot] = Texture;
		}
	}

	__inline void GSSetTexture (int slot, QTEXTURE *Texture)
	{
		if (this->GSTextures[slot] != Texture)
		{
			this->OnStateChange ();
			d3d_Device->GSSetShaderResources (slot, 1, &Texture->SRV);
			this->GSTextures[slot] = Texture;
		}
	}

	__inline void PSSetTexture (int slot, QTEXTURE *Texture)
	{
		if (this->PSTextures[slot] != Texture)
		{
			this->OnStateChange ();
			d3d_Device->PSSetShaderResources (slot, 1, &Texture->SRV);
			this->PSTextures[slot] = Texture;
		}
	}

	__inline void VSSetShader (ID3D10VertexShader *vs)
	{
		if (this->VertexShader != vs)
		{
			this->OnStateChange ();
			d3d_Device->VSSetShader (vs);
			this->VertexShader = vs;
		}}

	__inline void GSSetShader (ID3D10GeometryShader *gs)
	{
		if (this->GeometryShader != gs)
		{
			this->OnStateChange ();
			d3d_Device->GSSetShader (gs);
			this->GeometryShader = gs;
		}
	}

	__inline void PSSetShader (ID3D10PixelShader *ps)
	{
		if (this->PixelShader != ps)
		{
			this->OnStateChange ();
			d3d_Device->PSSetShader (ps);
			this->PixelShader = ps;
		}
	}

	__inline void IASetLayout (ID3D10InputLayout *lo)
	{
		if (this->InputLayout != lo)
		{
			this->OnStateChange ();
			d3d_Device->IASetInputLayout (lo);
			this->InputLayout = lo;
		}
	}

	void SetStateChangeCallback (xcommand_t cbfunc = QSTATEFILTER::NoStateChangeCallback);
	void UnbindStreams (void);
	void UnbindState (void);

private:
	static void NoStateChangeCallback (void) {}

	ID3D10BlendState *BlendState;
	ID3D10DepthStencilState *DepthState;
	UINT StencilRef;

	QTEXTURE *VSTextures[128];
	QTEXTURE *GSTextures[128];
	QTEXTURE *PSTextures[128];

	/*
	__inline void PSSetTexture (UINT Stage, IDirect3DBaseTexture9 *Texture = NULL)
	{
		if (this->PSCurrentTexture[Stage] != Texture)
		{
			this->OnStateChange ();
			d3d_Device->SetTexture (Stage, Texture);
			this->PSCurrentTexture[Stage] = Texture;
		}
	}

	__inline void SetStreamSource (UINT Stream, IDirect3DVertexBuffer9 *Buffer = NULL, UINT Offset = 0, UINT Stride = 0, UINT Freq = 1)
	{
		if (this->VertexStream[Stream].Buffer != Buffer ||
			this->VertexStream[Stream].Offset != Offset ||
			this->VertexStream[Stream].Stride != Stride)
		{
			this->OnStateChange ();
			d3d_Device->SetStreamSource (Stream, Buffer, Offset, Stride);
			this->VertexStream[Stream].Buffer = Buffer;
			this->VertexStream[Stream].Offset = Offset;
			this->VertexStream[Stream].Stride = Stride;
		}

		if (this->VertexStream[Stream].Freq != Freq)
		{
			this->OnStateChange ();
			d3d_Device->SetStreamSourceFreq (Stream, Freq);
			this->VertexStream[Stream].Freq = Freq;
		}
	}

	__inline void SetIndices (IDirect3DIndexBuffer9 *Buffer = NULL)
	{
		if (this->IndexBuffer != Buffer)
		{
			this->OnStateChange ();
			d3d_Device->SetIndices (Buffer);
			this->IndexBuffer = Buffer;
		}
	}

	__inline void VSSetLayout (IDirect3DVertexDeclaration9 *vd = NULL)
	{
		if (this->VertexDeclaration != vd)
		{
			this->OnStateChange ();
			d3d_Device->SetVertexDeclaration (vd);
			this->VertexDeclaration = vd;
		}
	}

	__inline void VSSetShader (ID3D10VertexShader *vs = NULL)
	{
		if (this->VertexShader != vs)
		{
			this->OnStateChange ();
			d3d_Device->SetVertexShader (vs);
			this->VertexShader = vs;
		}
	}

	__inline void PSSetShader (ID3D10PixelShader *ps = NULL)
	{
		if (this->PixelShader != ps)
		{
			this->OnStateChange ();
			d3d_Device->SetPixelShader (ps);
			this->PixelShader = ps;
		}
	}

	__inline void SetRenderState (D3DRENDERSTATETYPE state, DWORD value)
	{
		if (this->RenderState[(int) state] != value)
		{
			this->OnStateChange ();
			d3d_Device->SetRenderState (state, value);
			this->RenderState[(int) state] = value;
		}
	}

	__inline void SetRenderStatef (D3DRENDERSTATETYPE state, float value)
	{
		this->SetRenderState (state, *((DWORD *) &value));
	}

	void SetRenderState (renderstate_t *rs);
	void PSSetSampler (DWORD Stage, D3DTEXTUREFILTERTYPE MinMagFilter, D3DTEXTUREFILTERTYPE MipFilter, D3DTEXTUREADDRESS AddressMode, DWORD Anisotropy = 1);

	__inline void VSSetConstantf (int reg, float a, float b = 0, float c = 0, float d = 0) const
	{
		float constant[4] = {a, b, c, d};
		this->VSSetConstant4fv (reg, constant);
	}

	__inline void VSSetConstant2fv (int reg, const float *c) const
	{
		float constant[4] = {c[0], c[1], 0, 0};
		this->VSSetConstant4fv (reg, constant);
	}

	__inline void VSSetConstant3fv (int reg, const float *c) const
	{
		float constant[4] = {c[0], c[1], c[2], 0};
		this->VSSetConstant4fv (reg, constant);
	}

	__inline void VSSetConstant4fv (int reg, const float *c, int numfloat4 = 1) const
	{
		this->OnStateChange ();
		d3d_Device->SetVertexShaderConstantF (reg, c, numfloat4);
	}

	__inline void VSSetConstantMatrix (int startreg, const QMATRIX &m) const
	{
		this->OnStateChange ();
		d3d_Device->SetVertexShaderConstantF (startreg, m.Get (), 4);
	}

	__inline void PSSetConstantf (int reg, float a, float b = 0, float c = 0, float d = 0) const
	{
		float constant[4] = {a, b, c, d};
		this->PSSetConstant4fv (reg, constant);
	}

	__inline void PSSetConstant2fv (int reg, const float *c) const
	{
		float constant[4] = {c[0], c[1], 0, 0};
		this->PSSetConstant4fv (reg, constant);
	}

	__inline void PSSetConstant3fv (int reg, const float *c) const
	{
		float constant[4] = {c[0], c[1], c[2], 0};
		this->PSSetConstant4fv (reg, constant);
	}

	__inline void PSSetConstant4fv (int reg, const float *c, int numfloat4 = 1) const
	{
		this->OnStateChange ();
		d3d_Device->SetPixelShaderConstantF (reg, c, numfloat4);
	}

	__inline void PSSetConstantMatrix (int startreg, const QMATRIX &m) const
	{
		this->OnStateChange ();
		d3d_Device->SetPixelShaderConstantF (startreg, m.Get (), 4);
	}

	void SetStateChangeCallback (xcommand_t cbfunc = QSTATEFILTER::NoStateChangeCallback);
	void UnbindStreams (void);
	void UnbindState (void);

private:
	static void NoStateChangeCallback (void) {}

	IDirect3DBaseTexture9 *PSCurrentTexture[16];
	DWORD RenderState[256];
	IDirect3DVertexDeclaration9 *VertexDeclaration;
	IDirect3DIndexBuffer9 *IndexBuffer;

	renderstate_t *NewRenderState;

	struct vertexstream_t
	{
		IDirect3DVertexBuffer9 *Buffer;
		UINT Offset;
		UINT Stride;
		UINT Freq;
	} VertexStream[16];

	struct samplerstate_t
	{
		D3DTEXTUREFILTERTYPE MinFilter;
		D3DTEXTUREFILTERTYPE MagFilter;
		D3DTEXTUREFILTERTYPE MipFilter;
		D3DTEXTUREADDRESS AddressMode;
		DWORD Anisotropy;
	} PSSamplerState[16];
	*/

	ID3D10InputLayout *InputLayout;
	ID3D10VertexShader *VertexShader;
	ID3D10GeometryShader *GeometryShader;
	ID3D10PixelShader *PixelShader;

	xcommand_t OnStateChange;
};


extern QSTATEFILTER *d3d_State;

